import { Update } from "../shared/reactFlags";
import { isStrOrNum } from "../shared/utils";
import {
  deleteOldFiber,
  deleteRemainingChildren,
  mapRemainingChildren,
  placeChild,
  same,
} from "./reconcilerChildrenAssistent";
import { createFiber, IFiber } from "./updateContainer";

/**
 * @param returnFiber 父fiber
 * @param newChildren 父fiber的子节点
 * @returns
 */
export function reconcileChildren(returnFiber: IFiber, newChildren: any | any[]) {
  // 如果子元素是字符串则不需要处理
  if (isStrOrNum(newChildren)) {
    return;
  }

  // 统一使用数组操作
  const children = Array.isArray(newChildren) ? newChildren : [newChildren];

  // 上一个fiber
  let preFiber: IFiber | null = null;

  // 上一个旧fiber
  let oldFiber: IFiber | null = returnFiber.alternate?.child;

  // 记录children的索引
  let index = 0;

  // 上一次插入dom元素最远的索引
  let lastPlacedIndex = 0;

  // 是否有副作用
  const shouldTrackSideEffects = Boolean(returnFiber.alternate); // 是否需要追踪副作用

  // 该变量有两个作用：1.存储下一个旧的 fiber 对象 2.临时存储当前的旧的 fiber 对象
  let nextOldFiber = null;

  const len = children.length;
  // 1. 查看是否有能复用的节点
  for (; oldFiber && index < len; index++) {
    // 取出一个vnode
    const newChild = children[index];
    if (!newChild) {
      continue;
    }

    // 当进行删除节点时，fiber的index可能会比当前的index大，需要进行额外操作
    // oldFiber: 1(0) 2(1) 3(2) 4(3) 5(4)
    // newVnode: 5(4) 4(3)
    if (oldFiber.index > index) {
      nextOldFiber = oldFiber;
      oldFiber = null;
    } else {
      nextOldFiber = oldFiber.sibling;
    }

    // 判断是否能够复用
    const isSame = same(newChild, oldFiber);

    if (!isSame) {
      // 证明不能复用
      // 若oldfiber 为 null，则需要将原本的值进行还原，方便后续处理
      if (oldFiber === null) {
        oldFiber = nextOldFiber;
      }
      // 直接跳出循环
      break;
    }

    // 走到这里证明可以复用
    const fiber = createFiber(newChild, returnFiber);

    // 复用旧fiber的DOM以及其他信息
    Object.assign(fiber, {
      stateNode: oldFiber?.stateNode,
      alternate: oldFiber,
      flags: Update,
    });

    // 更新 lastIndex
    lastPlacedIndex = placeChild(fiber, lastPlacedIndex, index, shouldTrackSideEffects);

    _updateFiberLink(fiber);
    oldFiber = nextOldFiber;
  }

  // 2. 经过上面for循环处理走到这里有两种情况：oldFiber为null证明是首次渲染 index === len证明是更新
  if (index === len && oldFiber) {
    // 证明是更新的情况，并且旧节点还有剩余
    deleteRemainingChildren(returnFiber, oldFiber);
    return;
  }

  // 3. 处理首次渲染
  if (!oldFiber) {
    for (; index < len; index++) {
      const child = children[index];

      if (child === null) {
        continue;
      }
      // 将vnode转成fiber
      const fiber = createFiber(child, returnFiber);

      lastPlacedIndex = placeChild(fiber, lastPlacedIndex, index, shouldTrackSideEffects);

      _updateFiberLink(fiber);
    }
    return;
  }

  // 4. 新旧节点都有剩余的情况
  const existingChildren = mapRemainingChildren(oldFiber);
  for (; index < len; index++) {
    // 将旧节点放入到map中
    const child = newChildren[index];
    if (!child) {
      continue;
    }

    const newFiber = createFiber(child, returnFiber);
    // 看是否有能够复用的节点
    const reuseFiber = existingChildren.get(newFiber.key || newFiber.index);

    if (reuseFiber) {
      Object.assign(newFiber, {
        stateNode: reuseFiber?.stateNode,
        alternate: reuseFiber,
        flags: Update,
      });

      existingChildren.delete(newFiber.key || newFiber.index);
    }

    _updateFiberLink(newFiber);
  }
  /**
   * 要追踪副作用证明是更新流程
   */
  if (shouldTrackSideEffects) {
    // 若map中还有剩余节点，那么标记为删除
    existingChildren.forEach((item) => {
      deleteOldFiber(returnFiber, item);
    });
  }

  /**
   * 更新链表
   */
  function _updateFiberLink(newFiber: IFiber) {
    if (preFiber === null) {
      returnFiber.child = newFiber;
    } else {
      (preFiber as IFiber).sibling = newFiber;
    }

    preFiber = newFiber;
  }
}
